// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:kernel/ast.dart';
import 'package:kernel/reference_from_index.dart';
import 'package:kernel/src/bounds_checks.dart' show VarianceCalculationValue;

import '../base/messages.dart';
import '../base/modifiers.dart';
import '../base/name_space.dart';
import '../base/scope.dart';
import '../base/uri_offset.dart';
import '../builder/builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/type_builder.dart';
import '../fragment/constructor/declaration.dart';
import '../fragment/constructor/encoding.dart';
import '../fragment/factory/declaration.dart';
import '../fragment/factory/encoding.dart';
import '../fragment/field/declaration.dart';
import '../fragment/fragment.dart';
import '../fragment/getter/declaration.dart';
import '../fragment/method/declaration.dart';
import '../fragment/method/encoding.dart';
import '../fragment/setter/declaration.dart';
import 'name_scheme.dart';
import 'name_space_builder.dart';
import 'source_class_builder.dart';
import 'source_constructor_builder.dart';
import 'source_enum_builder.dart';
import 'source_extension_builder.dart';
import 'source_extension_type_declaration_builder.dart';
import 'source_factory_builder.dart';
import 'source_library_builder.dart';
import 'source_loader.dart';
import 'source_method_builder.dart';
import 'source_property_builder.dart';
import 'source_type_alias_builder.dart';
import 'source_type_parameter_builder.dart';
import 'type_parameter_factory.dart';

/// Reports an error if [declaration] is augmenting.
///
/// This is called when the first [_PreBuilder] is created, meaning that the
/// augmentation didn't correspond to an introductory declaration.
void _checkAugmentation(
  ProblemReporting problemReporting,
  _Declaration declaration,
) {
  if (declaration.isAugment) {
    Message message;
    switch (declaration.kind) {
      case _DeclarationKind.Class:
        message = declaration.inPatch
            ? codeUnmatchedPatchClass.withArgumentsOld(declaration.displayName)
            :
              // Coverage-ignore(suite): Not run.
              codeUnmatchedAugmentationClass.withArgumentsOld(
                declaration.displayName,
              );
      case _DeclarationKind.Constructor:
      case _DeclarationKind.Factory:
      case _DeclarationKind.Method:
      case _DeclarationKind.Property:
        if (declaration.inLibrary) {
          message = declaration.inPatch
              ? codeUnmatchedPatchLibraryMember.withArgumentsOld(
                  declaration.displayName,
                )
              :
                // Coverage-ignore(suite): Not run.
                codeUnmatchedAugmentationLibraryMember.withArgumentsOld(
                  declaration.displayName,
                );
        } else {
          message = declaration.inPatch
              ? codeUnmatchedPatchClassMember.withArgumentsOld(
                  declaration.displayName,
                )
              :
                // Coverage-ignore(suite): Not run.
                codeUnmatchedAugmentationClassMember.withArgumentsOld(
                  declaration.displayName,
                );
        }
      case _DeclarationKind.Mixin:
      case _DeclarationKind.NamedMixinApplication:
      case _DeclarationKind.Enum:
      case _DeclarationKind.Extension:
      // Coverage-ignore(suite): Not run.
      case _DeclarationKind.ExtensionType:
      // Coverage-ignore(suite): Not run.
      case _DeclarationKind.Typedef:
        // TODO(johnniwinther): Specialize more messages.
        message = declaration.inPatch
            ? codeUnmatchedPatchDeclaration.withArgumentsOld(
                declaration.displayName,
              )
            :
              // Coverage-ignore(suite): Not run.
              codeUnmatchedAugmentationDeclaration.withArgumentsOld(
                declaration.displayName,
              );
    }
    problemReporting.addProblem2(message, declaration.uriOffset);
  }
}

class BuilderFactory {
  final ProblemReporting _problemReporting;
  final SourceLoader _loader;
  final BuilderRegistry _builderRegistry;
  final SourceLibraryBuilder _enclosingLibraryBuilder;
  final DeclarationBuilder? _declarationBuilder;
  final TypeParameterFactory _typeParameterFactory;
  final Map<SourceClassBuilder, TypeBuilder> _mixinApplications;
  final IndexedLibrary? _indexedLibrary;
  final ContainerType _containerType;
  final IndexedContainer? _indexedContainer;
  final ContainerName? _containerName;
  final bool _inLibrary;

  BuilderFactory({
    required ProblemReporting problemReporting,
    required SourceLoader loader,
    required BuilderRegistry builderRegistry,
    required SourceLibraryBuilder enclosingLibraryBuilder,
    DeclarationBuilder? declarationBuilder,
    required TypeParameterFactory typeParameterFactory,
    required Map<SourceClassBuilder, TypeBuilder> mixinApplications,
    required IndexedLibrary? indexedLibrary,
    required ContainerType containerType,
    IndexedContainer? indexedContainer,
    ContainerName? containerName,
  }) : _containerName = containerName,
       _indexedContainer = indexedContainer,
       _containerType = containerType,
       _indexedLibrary = indexedLibrary,
       _mixinApplications = mixinApplications,
       _typeParameterFactory = typeParameterFactory,
       _declarationBuilder = declarationBuilder,
       _enclosingLibraryBuilder = enclosingLibraryBuilder,
       _builderRegistry = builderRegistry,
       _loader = loader,
       _problemReporting = problemReporting,
       _inLibrary = declarationBuilder == null;

  void computeBuildersByName(
    String name, {
    List<Fragment>? fragments,
    SyntheticDeclaration? syntheticDeclaration,
  }) {
    List<_PreBuilder> nonConstructorPreBuilders = [];
    List<_PreBuilder> constructorPreBuilders = [];
    List<Fragment> unnamedFragments = [];

    if (syntheticDeclaration != null) {
      syntheticDeclaration.createDeclaration().registerPreBuilder(
        _problemReporting,
        nonConstructorPreBuilders,
        constructorPreBuilders,
      );
    }

    if (fragments != null) {
      for (int i = 0; i < fragments.length; i++) {
        Fragment fragment = fragments[i];
        _Declaration? declaration = _createDeclarationFromFragment(
          fragment,
          inLibrary: _inLibrary,
          unnamedFragments: unnamedFragments,
        );

        declaration?.registerPreBuilder(
          _problemReporting,
          nonConstructorPreBuilders,
          constructorPreBuilders,
        );
      }
    }

    for (int i = 0; i < nonConstructorPreBuilders.length; i++) {
      _PreBuilder preBuilder = nonConstructorPreBuilders[i];
      preBuilder.createBuilders(this);
    }
    for (int i = 0; i < constructorPreBuilders.length; i++) {
      _PreBuilder preBuilder = constructorPreBuilders[i];
      preBuilder.createBuilders(this);
    }
    for (int i = 0; i < unnamedFragments.length; i++) {
      Fragment fragment = unnamedFragments[i];
      _createBuilder(fragment);
    }
  }

  void _createBuilder(Fragment fragment, {List<Fragment>? augmentations}) {
    switch (fragment) {
      case TypedefFragment():
        _createTypedefBuilder(fragment);
      case ClassFragment():
        _createClassBuilder(fragment, augmentations);
      case MixinFragment():
        _createMixinBuilder(fragment);
      case NamedMixinApplicationFragment():
        _createNamedMixinApplicationBuilder(fragment);
      case EnumFragment():
        _createEnumBuilder(fragment);
      case ExtensionFragment():
        _createExtensionBuilder(fragment, augmentations);
      case ExtensionTypeFragment():
        _createExtensionTypeBuilder(fragment);
      case MethodFragment():
        _createMethodBuilder(fragment, augmentations);
      // Coverage-ignore(suite): Not run.
      case ConstructorFragment():
      case PrimaryConstructorFragment():
      case FactoryFragment():
      case FieldFragment():
      case PrimaryConstructorFieldFragment():
      case GetterFragment():
      case SetterFragment():
      case EnumElementFragment():
        throw new UnsupportedError('Unexpected fragment $fragment.');
    }
    if (augmentations != null) {
      for (Fragment augmentation in augmentations) {
        // Coverage-ignore-block(suite): Not run.
        _createBuilder(augmentation);
      }
    }
  }

  void _createClassBuilder(
    ClassFragment fragment,
    List<Fragment>? augmentations,
  ) {
    String name = fragment.name;
    DeclarationNameSpaceBuilder nameSpaceBuilder = fragment
        .toDeclarationNameSpaceBuilder();
    ClassDeclaration introductoryDeclaration = new RegularClassDeclaration(
      fragment,
    );
    List<SourceNominalParameterBuilder>? nominalParameters =
        _typeParameterFactory.createNominalParameterBuilders(
          fragment.typeParameters,
        );
    fragment.nominalParameterNameSpace.addTypeParameters(
      _problemReporting,
      nominalParameters,
      ownerName: fragment.name,
      allowNameConflict: false,
    );

    Modifiers modifiers = fragment.modifiers;
    List<ClassDeclaration> augmentationDeclarations = [];
    if (augmentations != null) {
      int introductoryTypeParameterCount = fragment.typeParameters?.length ?? 0;
      for (Fragment augmentation in augmentations) {
        // Promote [augmentation] to [ClassFragment].
        augmentation as ClassFragment;

        // TODO(johnniwinther): Check that other modifiers are consistent.
        if (augmentation.modifiers.declaresConstConstructor) {
          modifiers |= Modifiers.DeclaresConstConstructor;
        }
        augmentationDeclarations.add(new RegularClassDeclaration(augmentation));
        nameSpaceBuilder.includeBuilders(
          augmentation.toDeclarationNameSpaceBuilder(),
        );

        int augmentationTypeParameterCount =
            augmentation.typeParameters?.length ?? 0;
        if (introductoryTypeParameterCount != augmentationTypeParameterCount) {
          _problemReporting.addProblem(
            codePatchClassTypeParametersMismatch,
            augmentation.nameOffset,
            name.length,
            augmentation.fileUri,
            context: [
              codePatchClassOrigin.withLocation(
                fragment.fileUri,
                fragment.nameOffset,
                name.length,
              ),
            ],
          );

          // Error recovery. Create fresh type parameters for the
          // augmentation.
          augmentation.nominalParameterNameSpace.addTypeParameters(
            _problemReporting,
            _typeParameterFactory.createNominalParameterBuilders(
              augmentation.typeParameters,
            ),
            ownerName: augmentation.name,
            allowNameConflict: false,
          );
        } else if (augmentation.typeParameters != null) {
          for (int index = 0; index < introductoryTypeParameterCount; index++) {
            SourceNominalParameterBuilder nominalParameterBuilder =
                nominalParameters![index];
            TypeParameterFragment typeParameterFragment =
                augmentation.typeParameters![index];
            nominalParameterBuilder.addAugmentingDeclaration(
              new RegularNominalParameterDeclaration(typeParameterFragment),
            );
            typeParameterFragment.builder = nominalParameterBuilder;
          }
          augmentation.nominalParameterNameSpace.addTypeParameters(
            _problemReporting,
            nominalParameters,
            ownerName: augmentation.name,
            allowNameConflict: false,
          );
        }
      }
    }
    IndexedClass? indexedClass = _indexedLibrary?.lookupIndexedClass(name);
    SourceClassBuilder classBuilder = new SourceClassBuilder(
      modifiers: modifiers,
      name: name,
      typeParameters: fragment.typeParameters?.builders,
      typeParameterScope: fragment.typeParameterScope,
      nameSpaceBuilder: nameSpaceBuilder,
      libraryBuilder: _enclosingLibraryBuilder,
      fileUri: fragment.fileUri,
      nameOffset: fragment.nameOffset,
      indexedClass: indexedClass,
      introductory: introductoryDeclaration,
      augmentations: augmentationDeclarations,
    );
    fragment.builder = classBuilder;
    fragment.bodyScope.declarationBuilder = classBuilder;
    if (augmentations != null) {
      for (Fragment augmentation in augmentations) {
        augmentation as ClassFragment;
        augmentation.builder = classBuilder;
        augmentation.bodyScope.declarationBuilder = classBuilder;
      }
      augmentations.clear();
    }
    if (indexedClass != null) {
      _loader.referenceMap.registerNamedBuilder(
        indexedClass.reference,
        classBuilder,
      );
    }
    _builderRegistry.registerBuilder(
      declaration: classBuilder,
      uriOffset: fragment.uriOffset,
      inPatch: fragment.enclosingCompilationUnit.isPatch,
    );
  }

  void _createConstructorBuilderFromDeclarations(
    ConstructorDeclaration constructorDeclaration,
    List<ConstructorDeclaration> augmentationDeclarations, {
    required String name,
    required UriOffsetLength uriOffset,
    required bool isConst,
    required bool inPatch,
  }) {
    NameScheme nameScheme = new NameScheme(
      isInstanceMember: false,
      containerName: _containerName,
      containerType: _containerType,
      libraryName: _indexedLibrary != null
          ? new LibraryName(_indexedLibrary.library.reference)
          : _enclosingLibraryBuilder.libraryName,
    );

    ConstructorEncodingStrategy encodingStrategy =
        new ConstructorEncodingStrategy(_declarationBuilder!);

    ConstructorReferences constructorReferences = new ConstructorReferences(
      name: name,
      nameScheme: nameScheme,
      indexedContainer: _indexedContainer,
      loader: _loader,
      declarationBuilder: _declarationBuilder,
    );

    SourceConstructorBuilder constructorBuilder = new SourceConstructorBuilder(
      name: name,
      libraryBuilder: _enclosingLibraryBuilder,
      declarationBuilder: _declarationBuilder,
      fileUri: uriOffset.fileUri,
      fileOffset: uriOffset.fileOffset,
      constructorReferences: constructorReferences,
      nameScheme: nameScheme,
      introductory: constructorDeclaration,
      augmentations: augmentationDeclarations,
      isConst: isConst,
    );
    constructorReferences.registerReference(
      _loader.referenceMap,
      constructorBuilder,
    );

    constructorDeclaration.createEncoding(
      problemReporting: _problemReporting,
      loader: _loader,
      declarationBuilder: _declarationBuilder,
      constructorBuilder: constructorBuilder,
      typeParameterFactory: _typeParameterFactory,
      encodingStrategy: encodingStrategy,
    );
    for (ConstructorDeclaration augmentation in augmentationDeclarations) {
      augmentation.createEncoding(
        problemReporting: _problemReporting,
        loader: _loader,
        declarationBuilder: _declarationBuilder,
        constructorBuilder: constructorBuilder,
        typeParameterFactory: _typeParameterFactory,
        encodingStrategy: encodingStrategy,
      );
    }
    _builderRegistry.registerBuilder(
      declaration: constructorBuilder,
      uriOffset: uriOffset,
      inPatch: inPatch,
    );
  }

  _Declaration? _createDeclarationFromFragment(
    Fragment fragment, {
    required bool inLibrary,
    required List<Fragment> unnamedFragments,
  }) {
    switch (fragment) {
      case ClassFragment():
        return new _StandardFragmentDeclaration(
          _DeclarationKind.Class,
          fragment,
          displayName: fragment.name,
          isAugment: fragment.modifiers.isAugment,
          inPatch: fragment.enclosingCompilationUnit.isPatch,
          inLibrary: true,
        );
      case EnumFragment():
        return new _StandardFragmentDeclaration(
          _DeclarationKind.Enum,
          fragment,
          displayName: fragment.name,
          // TODO(johnniwinther): Support enum augmentations.
          isAugment: false,
          inPatch: fragment.enclosingCompilationUnit.isPatch,
          inLibrary: true,
        );
      case ExtensionTypeFragment():
        return new _StandardFragmentDeclaration(
          _DeclarationKind.ExtensionType,
          fragment,
          displayName: fragment.name,
          isAugment: fragment.modifiers.isAugment,
          inPatch: fragment.enclosingCompilationUnit.isPatch,
          inLibrary: true,
        );
      case MethodFragment():
        return new _StandardFragmentDeclaration(
          _DeclarationKind.Method,
          fragment,
          displayName: fragment.name,
          isAugment: fragment.modifiers.isAugment,
          isStatic: inLibrary || fragment.modifiers.isStatic,
          inPatch:
              fragment.enclosingDeclaration?.isPatch ??
              fragment.enclosingCompilationUnit.isPatch,
          inLibrary: inLibrary,
        );
      case MixinFragment():
        return new _StandardFragmentDeclaration(
          _DeclarationKind.Mixin,
          fragment,
          displayName: fragment.name,
          isAugment: fragment.modifiers.isAugment,
          inPatch: fragment.enclosingCompilationUnit.isPatch,
          inLibrary: true,
        );
      case NamedMixinApplicationFragment():
        return new _StandardFragmentDeclaration(
          _DeclarationKind.NamedMixinApplication,
          fragment,
          displayName: fragment.name,
          isAugment: fragment.modifiers.isAugment,
          inPatch: fragment.enclosingCompilationUnit.isPatch,
          inLibrary: true,
        );
      case TypedefFragment():
        return new _StandardFragmentDeclaration(
          _DeclarationKind.Typedef,
          fragment,
          displayName: fragment.name,
          // TODO(johnniwinther): Support typedef augmentations.
          isAugment: false,
          inPatch: fragment.enclosingCompilationUnit.isPatch,
          inLibrary: true,
        );
      case ExtensionFragment():
        if (!fragment.isUnnamed) {
          return new _StandardFragmentDeclaration(
            _DeclarationKind.Extension,
            fragment,
            displayName: fragment.name,
            isAugment: fragment.modifiers.isAugment,
            inPatch: fragment.enclosingCompilationUnit.isPatch,
            inLibrary: true,
          );
        } else {
          unnamedFragments.add(fragment);
          return null;
        }
      case FactoryFragment():
        return new _FactoryConstructorDeclaration(
          new FactoryDeclarationImpl(fragment),
          name: fragment.name,
          displayName: fragment.constructorName.fullName,
          isAugment: fragment.modifiers.isAugment,
          inPatch: fragment.enclosingDeclaration.isPatch,
          inLibrary: inLibrary,
          isConst: fragment.modifiers.isConst,
          uriOffset: fragment.uriOffset,
        );
      case ConstructorFragment():
        return new _GenerativeConstructorDeclaration(
          new RegularConstructorDeclaration(fragment),
          name: fragment.name,
          displayName: fragment.constructorName.fullName,
          isAugment: fragment.modifiers.isAugment,
          inPatch: fragment.enclosingDeclaration.isPatch,
          inLibrary: inLibrary,
          isConst: fragment.modifiers.isConst,
          uriOffset: fragment.uriOffset,
        );
      case PrimaryConstructorFragment():
        return new _GenerativeConstructorDeclaration(
          new PrimaryConstructorDeclaration(fragment),
          name: fragment.name,
          displayName: fragment.constructorName.fullName,
          isAugment: fragment.modifiers.isAugment,
          inPatch: fragment.enclosingDeclaration.isPatch,
          inLibrary: inLibrary,
          isConst: fragment.modifiers.isConst,
          uriOffset: fragment.uriOffset,
        );
      case FieldFragment():
        RegularFieldDeclaration declaration = new RegularFieldDeclaration(
          fragment,
        );
        return new _FieldDeclaration(
          displayName: fragment.name,
          isAugment: fragment.modifiers.isAugment,
          propertyKind: fragment.hasSetter
              ? _PropertyKind.Field
              : _PropertyKind.FinalField,
          isStatic: inLibrary || fragment.modifiers.isStatic,
          inPatch:
              fragment.enclosingDeclaration?.isPatch ??
              fragment.enclosingCompilationUnit.isPatch,
          inLibrary: inLibrary,
          uriOffset: fragment.uriOffset,
          declarations: new _PropertyDeclarations(
            field: declaration,
            getter: declaration,
            setter: fragment.hasSetter ? declaration : null,
          ),
        );
      case PrimaryConstructorFieldFragment():
        PrimaryConstructorFieldDeclaration declaration =
            new PrimaryConstructorFieldDeclaration(fragment);
        return new _FieldDeclaration(
          displayName: fragment.name,
          isAugment: false,
          propertyKind: _PropertyKind.FinalField,
          isStatic: false,
          inPatch: fragment.enclosingDeclaration.isPatch,
          inLibrary: false,
          uriOffset: fragment.uriOffset,
          declarations: new _PropertyDeclarations(
            field: declaration,
            getter: declaration,
          ),
        );
      case GetterFragment():
        return new _GetterDeclaration(
          displayName: fragment.name,
          isAugment: fragment.modifiers.isAugment,
          propertyKind: _PropertyKind.Getter,
          isStatic: inLibrary || fragment.modifiers.isStatic,
          inPatch:
              fragment.enclosingDeclaration?.isPatch ??
              fragment.enclosingCompilationUnit.isPatch,
          inLibrary: inLibrary,
          uriOffset: fragment.uriOffset,
          declarations: new _PropertyDeclarations(
            getter: new RegularGetterDeclaration(fragment),
          ),
        );
      case SetterFragment():
        return new _SetterDeclaration(
          displayName: fragment.name,
          isAugment: fragment.modifiers.isAugment,
          propertyKind: _PropertyKind.Setter,
          isStatic: inLibrary || fragment.modifiers.isStatic,
          inPatch:
              fragment.enclosingDeclaration?.isPatch ??
              fragment.enclosingCompilationUnit.isPatch,
          inLibrary: inLibrary,
          uriOffset: fragment.uriOffset,
          declarations: new _PropertyDeclarations(
            setter: new RegularSetterDeclaration(fragment),
          ),
        );
      case EnumElementFragment():
        EnumElementDeclaration declaration = new EnumElementDeclaration(
          fragment,
        );
        return new _FieldDeclaration(
          displayName: fragment.name,
          isAugment: false,
          propertyKind: _PropertyKind.FinalField,
          isStatic: true,
          inPatch: fragment.enclosingDeclaration.isPatch,
          inLibrary: inLibrary,
          uriOffset: fragment.uriOffset,
          declarations: new _PropertyDeclarations(
            field: declaration,
            getter: declaration,
          ),
        );
    }
  }

  void _createEnumBuilder(EnumFragment fragment) {
    IndexedClass? indexedClass = _indexedLibrary?.lookupIndexedClass(
      fragment.name,
    );
    List<SourceNominalParameterBuilder>? typeParameters = _typeParameterFactory
        .createNominalParameterBuilders(fragment.typeParameters);
    fragment.nominalParameterNameSpace.addTypeParameters(
      _problemReporting,
      typeParameters,
      ownerName: fragment.name,
      allowNameConflict: false,
    );
    SourceEnumBuilder enumBuilder = new SourceEnumBuilder(
      name: fragment.name,
      typeParameters: typeParameters,
      underscoreEnumTypeBuilder: _loader.target.underscoreEnumType,
      interfaceBuilders: fragment.interfaces,
      enumElements: fragment.enumElements,
      libraryBuilder: _enclosingLibraryBuilder,
      fileUri: fragment.fileUri,
      startOffset: fragment.startOffset,
      nameOffset: fragment.nameOffset,
      endOffset: fragment.endOffset,
      indexedClass: indexedClass,
      typeParameterScope: fragment.typeParameterScope,
      nameSpaceBuilder: fragment.toDeclarationNameSpaceBuilder(),
      classDeclaration: new EnumDeclaration(
        fragment,
        _loader.target.underscoreEnumType,
      ),
    );
    fragment.builder = enumBuilder;
    fragment.bodyScope.declarationBuilder = enumBuilder;
    if (indexedClass != null) {
      _loader.referenceMap.registerNamedBuilder(
        indexedClass.reference,
        enumBuilder,
      );
    }
    _builderRegistry.registerBuilder(
      declaration: enumBuilder,
      uriOffset: fragment.uriOffset,
      inPatch: fragment.enclosingCompilationUnit.isPatch,
    );
  }

  void _createExtensionBuilder(
    ExtensionFragment fragment,
    List<Fragment>? augmentations,
  ) {
    DeclarationNameSpaceBuilder nameSpaceBuilder = fragment
        .toDeclarationNameSpaceBuilder();
    List<SourceNominalParameterBuilder>? nominalParameters =
        _typeParameterFactory.createNominalParameterBuilders(
          fragment.typeParameters,
        );
    fragment.nominalParameterNameSpace.addTypeParameters(
      _problemReporting,
      nominalParameters,
      ownerName: fragment.name,
      allowNameConflict: false,
    );

    List<ExtensionFragment> augmentationFragments = [];
    if (augmentations != null) {
      int introductoryTypeParameterCount = fragment.typeParameters?.length ?? 0;
      int nameLength = fragment.isUnnamed ? noLength : fragment.name.length;

      for (Fragment augmentation in augmentations) {
        // Promote [augmentation] to [ExtensionFragment].
        augmentation as ExtensionFragment;

        augmentationFragments.add(augmentation);
        nameSpaceBuilder.includeBuilders(
          augmentation.toDeclarationNameSpaceBuilder(),
        );

        int augmentationTypeParameterCount =
            augmentation.typeParameters?.length ?? 0;
        if (introductoryTypeParameterCount != augmentationTypeParameterCount) {
          _problemReporting.addProblem(
            codePatchExtensionTypeParametersMismatch,
            augmentation.nameOrExtensionOffset,
            nameLength,
            augmentation.fileUri,
            context: [
              codePatchExtensionOrigin.withLocation(
                fragment.fileUri,
                fragment.nameOrExtensionOffset,
                nameLength,
              ),
            ],
          );

          // Error recovery. Create fresh type parameters for the
          // augmentation.
          augmentation.nominalParameterNameSpace.addTypeParameters(
            _problemReporting,
            _typeParameterFactory.createNominalParameterBuilders(
              augmentation.typeParameters,
            ),
            ownerName: augmentation.name,
            allowNameConflict: false,
          );
        } else if (augmentation.typeParameters != null) {
          for (int index = 0; index < introductoryTypeParameterCount; index++) {
            SourceNominalParameterBuilder nominalParameterBuilder =
                nominalParameters![index];
            TypeParameterFragment typeParameterFragment =
                augmentation.typeParameters![index];
            nominalParameterBuilder.addAugmentingDeclaration(
              new RegularNominalParameterDeclaration(typeParameterFragment),
            );
            typeParameterFragment.builder = nominalParameterBuilder;
          }
          augmentation.nominalParameterNameSpace.addTypeParameters(
            _problemReporting,
            nominalParameters,
            ownerName: augmentation.name,
            allowNameConflict: false,
          );
        }
      }
      augmentations.clear();
    }
    Reference? reference;
    if (!fragment.extensionName.isUnnamedExtension) {
      reference = _indexedLibrary?.lookupExtension(fragment.name);
    }
    SourceExtensionBuilder extensionBuilder = new SourceExtensionBuilder(
      enclosingLibraryBuilder: _enclosingLibraryBuilder,
      fileUri: fragment.fileUri,
      startOffset: fragment.startOffset,
      nameOffset: fragment.nameOrExtensionOffset,
      endOffset: fragment.endOffset,
      introductory: fragment,
      augmentations: augmentationFragments,
      nameSpaceBuilder: nameSpaceBuilder,
      reference: reference,
    );
    if (reference != null) {
      _loader.referenceMap.registerNamedBuilder(reference, extensionBuilder);
    }
    _builderRegistry.registerBuilder(
      declaration: extensionBuilder,
      uriOffset: fragment.uriOffset,
      inPatch: fragment.enclosingCompilationUnit.isPatch,
    );
  }

  void _createExtensionTypeBuilder(ExtensionTypeFragment fragment) {
    IndexedContainer? indexedContainer = _indexedLibrary
        ?.lookupIndexedExtensionTypeDeclaration(fragment.name);
    List<PrimaryConstructorFieldFragment> primaryConstructorFields =
        fragment.primaryConstructorFields;
    PrimaryConstructorFieldFragment? representationFieldFragment;
    if (primaryConstructorFields.isNotEmpty) {
      representationFieldFragment = primaryConstructorFields.first;
    }
    _typeParameterFactory.createNominalParameterBuilders(
      fragment.typeParameters,
    );
    fragment.nominalParameterNameSpace.addTypeParameters(
      _problemReporting,
      fragment.typeParameters?.builders,
      ownerName: fragment.name,
      allowNameConflict: false,
    );
    SourceExtensionTypeDeclarationBuilder extensionTypeDeclarationBuilder =
        new SourceExtensionTypeDeclarationBuilder(
          name: fragment.name,
          enclosingLibraryBuilder: _enclosingLibraryBuilder,
          constructorReferences: fragment.constructorReferences,
          fileUri: fragment.fileUri,
          startOffset: fragment.startOffset,
          nameOffset: fragment.nameOffset,
          endOffset: fragment.endOffset,
          fragment: fragment,
          indexedContainer: indexedContainer,
          representationFieldFragment: representationFieldFragment,
        );
    if (indexedContainer?.reference != null) {
      _loader.referenceMap.registerNamedBuilder(
        indexedContainer!.reference,
        extensionTypeDeclarationBuilder,
      );
    }
    _builderRegistry.registerBuilder(
      declaration: extensionTypeDeclarationBuilder,
      uriOffset: fragment.uriOffset,
      inPatch: fragment.enclosingCompilationUnit.isPatch,
    );
  }

  void _createFactoryBuilderFromDeclarations(
    FactoryDeclaration introductory,
    List<FactoryDeclaration> augmentations, {
    required String name,
    required bool isConst,
    required UriOffsetLength uriOffset,
    required bool inPatch,
  }) {
    FactoryEncodingStrategy encodingStrategy = new FactoryEncodingStrategy(
      _declarationBuilder!,
    );

    NameScheme nameScheme = new NameScheme(
      containerName: _containerName,
      containerType: _containerType,
      isInstanceMember: false,
      libraryName: _indexedLibrary != null
          ? new LibraryName(_indexedLibrary.library.reference)
          : _enclosingLibraryBuilder.libraryName,
    );

    FactoryReferences factoryReferences = new FactoryReferences(
      name: name,
      nameScheme: nameScheme,
      indexedContainer: _indexedContainer,
      loader: _loader,
      declarationBuilder: _declarationBuilder,
    );

    bool isRedirectingFactory = introductory.isRedirectingFactory;
    for (FactoryDeclaration augmentation in augmentations) {
      if (augmentation.isRedirectingFactory) {
        isRedirectingFactory = true;
      }
    }

    SourceFactoryBuilder factoryBuilder = new SourceFactoryBuilder(
      name: name,
      libraryBuilder: _enclosingLibraryBuilder,
      declarationBuilder: _declarationBuilder,
      fileUri: uriOffset.fileUri,
      fileOffset: uriOffset.fileOffset,
      factoryReferences: factoryReferences,
      nameScheme: nameScheme,
      introductory: introductory,
      augmentations: augmentations,
      isConst: isConst,
    );
    if (isRedirectingFactory) {
      (_enclosingLibraryBuilder.redirectingFactoryBuilders ??= []).add(
        factoryBuilder,
      );
    }
    introductory.createEncoding(
      problemReporting: _problemReporting,
      declarationBuilder: _declarationBuilder,
      factoryBuilder: factoryBuilder,
      typeParameterFactory: _typeParameterFactory,
      encodingStrategy: encodingStrategy,
    );
    for (FactoryDeclaration augmentation in augmentations) {
      augmentation.createEncoding(
        problemReporting: _problemReporting,
        declarationBuilder: _declarationBuilder,
        factoryBuilder: factoryBuilder,
        typeParameterFactory: _typeParameterFactory,
        encodingStrategy: encodingStrategy,
      );
    }

    factoryReferences.registerReference(_loader.referenceMap, factoryBuilder);
    _builderRegistry.registerBuilder(
      declaration: factoryBuilder,
      uriOffset: uriOffset,
      inPatch: inPatch,
    );
  }

  void _createMethodBuilder(
    MethodFragment fragment,
    List<Fragment>? augmentations,
  ) {
    String name = fragment.name;
    final bool isInstanceMember =
        _containerType != ContainerType.Library && !fragment.modifiers.isStatic;

    _typeParameterFactory.createNominalParameterBuilders(
      fragment.declaredTypeParameters,
    );

    MethodEncodingStrategy encodingStrategy = new MethodEncodingStrategy(
      _declarationBuilder,
      isInstanceMember: isInstanceMember,
    );

    ProcedureKind kind = fragment.isOperator
        ? ProcedureKind.Operator
        : ProcedureKind.Method;

    final bool isExtensionMember = _containerType == ContainerType.Extension;
    final bool isExtensionTypeMember =
        _containerType == ContainerType.ExtensionType;

    NameScheme nameScheme = new NameScheme(
      containerName: _containerName,
      containerType: _containerType,
      isInstanceMember: isInstanceMember,
      libraryName: _indexedLibrary != null
          ? new LibraryName(_indexedLibrary.library.reference)
          : _enclosingLibraryBuilder.libraryName,
    );

    Reference? procedureReference;
    Reference? tearOffReference;
    IndexedContainer? indexedContainer = _indexedContainer ?? _indexedLibrary;

    if (indexedContainer != null) {
      Name nameToLookup = nameScheme.getProcedureMemberName(kind, name).name;
      procedureReference = indexedContainer.lookupGetterReference(nameToLookup);
      if ((isExtensionMember || isExtensionTypeMember) &&
          kind == ProcedureKind.Method) {
        tearOffReference = indexedContainer.lookupGetterReference(
          nameScheme.getProcedureMemberName(ProcedureKind.Getter, name).name,
        );
      }
    }

    Modifiers modifiers = fragment.modifiers;
    MethodDeclaration introductoryDeclaration = new MethodDeclarationImpl(
      fragment,
    );

    List<MethodDeclaration> augmentationDeclarations = [];
    if (augmentations != null) {
      for (Fragment augmentation in augmentations) {
        // Promote [augmentation] to [MethodFragment].
        augmentation as MethodFragment;

        augmentationDeclarations.add(new MethodDeclarationImpl(augmentation));

        _typeParameterFactory.createNominalParameterBuilders(
          augmentation.declaredTypeParameters,
        );

        if (!(augmentation.modifiers.isAbstract ||
            augmentation.modifiers.isExternal)) {
          modifiers -= Modifiers.Abstract;
          modifiers -= Modifiers.External;
        }
      }
    }

    SourceMethodBuilder methodBuilder = new SourceMethodBuilder(
      fileUri: fragment.fileUri,
      fileOffset: fragment.nameOffset,
      name: name,
      libraryBuilder: _enclosingLibraryBuilder,
      declarationBuilder: _declarationBuilder,
      isStatic: modifiers.isStatic,
      modifiers: modifiers,
      introductory: introductoryDeclaration,
      augmentations: augmentationDeclarations,
      nameScheme: nameScheme,
      reference: procedureReference,
      tearOffReference: tearOffReference,
    );
    fragment.builder = methodBuilder;
    if (augmentations != null) {
      for (Fragment augmentation in augmentations) {
        // Promote [augmentation] to [MethodFragment].
        augmentation as MethodFragment;

        augmentation.builder = methodBuilder;
      }
      augmentations.clear();
    }
    introductoryDeclaration.createEncoding(
      _problemReporting,
      methodBuilder,
      encodingStrategy,
      _typeParameterFactory,
    );
    for (MethodDeclaration augmentation in augmentationDeclarations) {
      augmentation.createEncoding(
        _problemReporting,
        methodBuilder,
        encodingStrategy,
        _typeParameterFactory,
      );
    }

    if (procedureReference != null) {
      _loader.referenceMap.registerNamedBuilder(
        procedureReference,
        methodBuilder,
      );
    }
    _builderRegistry.registerBuilder(
      declaration: methodBuilder,
      uriOffset: fragment.uriOffset,
      inPatch:
          fragment.enclosingDeclaration?.isPatch ??
          fragment.enclosingCompilationUnit.isPatch,
    );
  }

  void _createMixinBuilder(MixinFragment fragment) {
    IndexedClass? indexedClass = _indexedLibrary?.lookupIndexedClass(
      fragment.name,
    );
    _typeParameterFactory.createNominalParameterBuilders(
      fragment.typeParameters,
    );
    List<SourceNominalParameterBuilder>? typeParameters =
        fragment.typeParameters?.builders;
    fragment.nominalParameterNameSpace.addTypeParameters(
      _problemReporting,
      typeParameters,
      ownerName: fragment.name,
      allowNameConflict: false,
    );
    SourceClassBuilder mixinBuilder = new SourceClassBuilder(
      modifiers: fragment.modifiers,
      name: fragment.name,
      typeParameters: typeParameters,
      typeParameterScope: fragment.typeParameterScope,
      nameSpaceBuilder: fragment.toDeclarationNameSpaceBuilder(),
      libraryBuilder: _enclosingLibraryBuilder,
      fileUri: fragment.fileUri,
      nameOffset: fragment.nameOffset,
      indexedClass: indexedClass,
      introductory: new MixinDeclaration(fragment),
    );
    fragment.builder = mixinBuilder;
    fragment.bodyScope.declarationBuilder = mixinBuilder;
    if (indexedClass != null) {
      _loader.referenceMap.registerNamedBuilder(
        indexedClass.reference,
        mixinBuilder,
      );
    }
    _builderRegistry.registerBuilder(
      declaration: mixinBuilder,
      uriOffset: fragment.uriOffset,
      inPatch: fragment.enclosingCompilationUnit.isPatch,
    );
  }

  void _createNamedMixinApplicationBuilder(
    NamedMixinApplicationFragment fragment,
  ) {
    List<TypeBuilder> mixins = fragment.mixins.toList();
    TypeBuilder mixin = mixins.removeLast();
    ClassDeclaration classDeclaration = new NamedMixinApplication(
      fragment,
      mixins,
    );

    String name = fragment.name;

    IndexedClass? referencesFromIndexedClass = _indexedLibrary
        ?.lookupIndexedClass(name);

    _typeParameterFactory.createNominalParameterBuilders(
      fragment.typeParameters,
    );
    fragment.nominalParameterNameSpace.addTypeParameters(
      _problemReporting,
      fragment.typeParameters?.builders,
      ownerName: name,
      allowNameConflict: false,
    );
    LookupScope typeParameterScope = TypeParameterScope.fromList(
      fragment.enclosingScope,
      fragment.typeParameters?.builders,
    );
    DeclarationNameSpaceBuilder nameSpaceBuilder =
        new DeclarationNameSpaceBuilder.empty();
    SourceClassBuilder classBuilder = new SourceClassBuilder(
      modifiers: fragment.modifiers | Modifiers.NamedMixinApplication,
      name: name,
      typeParameters: fragment.typeParameters?.builders,
      typeParameterScope: typeParameterScope,
      nameSpaceBuilder: nameSpaceBuilder,
      libraryBuilder: _enclosingLibraryBuilder,
      fileUri: fragment.fileUri,
      nameOffset: fragment.nameOffset,
      indexedClass: referencesFromIndexedClass,
      mixedInTypeBuilder: mixin,
      introductory: classDeclaration,
    );
    _mixinApplications[classBuilder] = mixin;
    fragment.builder = classBuilder;
    if (referencesFromIndexedClass != null) {
      _loader.referenceMap.registerNamedBuilder(
        referencesFromIndexedClass.reference,
        classBuilder,
      );
    }
    _builderRegistry.registerBuilder(
      declaration: classBuilder,
      uriOffset: fragment.uriOffset,
      inPatch: fragment.enclosingCompilationUnit.isPatch,
    );
  }

  void _createPropertyBuilder({
    required String name,
    required UriOffsetLength uriOffset,
    required FieldDeclaration? fieldDeclaration,
    required GetterDeclaration? getterDeclaration,
    required List<GetterDeclaration> getterAugmentations,
    required SetterDeclaration? setterDeclaration,
    required List<SetterDeclaration> setterAugmentations,
    required bool isStatic,
    required bool inPatch,
  }) {
    bool isInstanceMember =
        _containerType != ContainerType.Library && !isStatic;

    bool fieldIsLateWithLowering = false;
    if (fieldDeclaration != null) {
      fieldIsLateWithLowering =
          fieldDeclaration.isLate &&
          (_loader.target.backendTarget.isLateFieldLoweringEnabled(
                hasInitializer: fieldDeclaration.hasInitializer,
                isFinal: fieldDeclaration.isFinal,
                isStatic: !isInstanceMember,
              ) ||
              (_loader.target.backendTarget.useStaticFieldLowering &&
                  !isInstanceMember));
    }

    PropertyEncodingStrategy propertyEncodingStrategy =
        new PropertyEncodingStrategy(
          _declarationBuilder,
          isInstanceMember: isInstanceMember,
        );

    NameScheme nameScheme = new NameScheme(
      isInstanceMember: isInstanceMember,
      containerName: _containerName,
      containerType: _containerType,
      libraryName: _indexedLibrary != null
          ? new LibraryName(_indexedLibrary.reference)
          : _enclosingLibraryBuilder.libraryName,
    );
    IndexedContainer? indexedContainer = _indexedContainer ?? _indexedLibrary;

    PropertyReferences references = new PropertyReferences(
      name,
      nameScheme,
      indexedContainer,
      fieldIsLateWithLowering: fieldIsLateWithLowering,
    );

    SourcePropertyBuilder propertyBuilder = new SourcePropertyBuilder(
      fileUri: uriOffset.fileUri,
      fileOffset: uriOffset.fileOffset,
      name: name,
      libraryBuilder: _enclosingLibraryBuilder,
      declarationBuilder: _declarationBuilder,
      fieldDeclaration: fieldDeclaration,
      getterDeclaration: getterDeclaration,
      getterAugmentations: getterAugmentations,
      setterDeclaration: setterDeclaration,
      setterAugmentations: setterAugmentations,
      isStatic: isStatic,
      nameScheme: nameScheme,
      references: references,
    );

    fieldDeclaration?.createFieldEncoding(propertyBuilder);

    getterDeclaration?.createGetterEncoding(
      _problemReporting,
      propertyBuilder,
      propertyEncodingStrategy,
      _typeParameterFactory,
    );
    for (GetterDeclaration augmentation in getterAugmentations) {
      augmentation.createGetterEncoding(
        _problemReporting,
        propertyBuilder,
        propertyEncodingStrategy,
        _typeParameterFactory,
      );
    }

    setterDeclaration?.createSetterEncoding(
      _problemReporting,
      propertyBuilder,
      propertyEncodingStrategy,
      _typeParameterFactory,
    );
    for (SetterDeclaration augmentation in setterAugmentations) {
      augmentation.createSetterEncoding(
        _problemReporting,
        propertyBuilder,
        propertyEncodingStrategy,
        _typeParameterFactory,
      );
    }

    references.registerReference(_loader.referenceMap, propertyBuilder);

    _builderRegistry.registerBuilder(
      declaration: propertyBuilder,
      uriOffset: uriOffset,
      inPatch: inPatch,
    );
  }

  void _createTypedefBuilder(TypedefFragment fragment) {
    List<SourceNominalParameterBuilder>? nominalParameters =
        _typeParameterFactory.createNominalParameterBuilders(
          fragment.typeParameters,
        );
    if (nominalParameters != null) {
      for (SourceNominalParameterBuilder typeParameter in nominalParameters) {
        typeParameter.varianceCalculationValue =
            VarianceCalculationValue.pending;
      }
    }
    fragment.nominalParameterNameSpace.addTypeParameters(
      _problemReporting,
      nominalParameters,
      ownerName: fragment.name,
      allowNameConflict: true,
    );

    Reference? reference = _indexedLibrary?.lookupTypedef(fragment.name);
    SourceTypeAliasBuilder typedefBuilder = new SourceTypeAliasBuilder(
      name: fragment.name,
      enclosingLibraryBuilder: _enclosingLibraryBuilder,
      fileUri: fragment.fileUri,
      fileOffset: fragment.nameOffset,
      fragment: fragment,
      reference: reference,
    );
    if (reference != null) {
      _loader.referenceMap.registerNamedBuilder(reference, typedefBuilder);
    }
    _builderRegistry.registerBuilder(
      declaration: typedefBuilder,
      uriOffset: fragment.uriOffset,
      inPatch: fragment.enclosingCompilationUnit.isPatch,
    );
  }
}

abstract class BuilderRegistry {
  void registerBuilder({
    required NamedBuilder declaration,
    required UriOffsetLength uriOffset,
    required bool inPatch,
  });
}

class EnumValuesDeclaration extends _PropertyDeclaration
    implements SyntheticDeclaration {
  EnumValuesDeclaration({
    required String name,
    required UriOffsetLength uriOffset,
    required FieldDeclaration field,
    required GetterDeclaration getter,
  }) : super(
         propertyKind: _PropertyKind.FinalField,
         displayName: name,
         isAugment: false,
         inPatch: false,
         inLibrary: false,
         uriOffset: uriOffset,
         declarations: new _PropertyDeclarations(field: field, getter: getter),
       );
  @override
  _Declaration createDeclaration() {
    return this;
  }

  @override
  void reportDuplicateDeclaration(
    ProblemReporting problemReporting,
    _Declaration declaration,
  ) {
    problemReporting.addProblem2(
      codeEnumContainsValuesDeclaration,
      declaration.uriOffset,
    );
  }

  @override
  void reportStaticInstanceConflict(
    ProblemReporting problemReporting,
    _PropertyDeclaration declaration,
  ) {
    problemReporting.addProblem2(
      codeInstanceAndSynthesizedStaticConflict.withArgumentsOld(displayName),
      declaration.uriOffset,
    );
  }

  @override
  _PreBuilder _createPreBuilder() {
    return new _PropertyPreBuilder.forField(this);
  }
}

abstract class SyntheticDeclaration {
  _Declaration createDeclaration();
}

sealed class _ConstructorDeclaration extends _Declaration {
  final bool isConst;

  @override
  final UriOffsetLength uriOffset;

  _ConstructorDeclaration(
    super.kind, {
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    required this.isConst,
    required this.uriOffset,
  });

  @override
  void registerPreBuilder(
    ProblemReporting problemReporting,
    List<_PreBuilder> nonConstructorPreBuilders,
    List<_PreBuilder> constructorPreBuilders,
  ) {
    _addPreBuilder(
      problemReporting,
      constructorPreBuilders,
      nonConstructorPreBuilders,
    );
  }
}

/// [_PreBuilder] for generative and factory constructors.
sealed class _ConstructorPreBuilder<T extends _ConstructorDeclaration>
    extends _PreBuilder {
  final T _declaration;
  final List<T> _augmentations = [];

  // TODO(johnniwinther): Report error if [fragment] is augmenting.
  _ConstructorPreBuilder(this._declaration);

  @override
  bool absorbFragment(
    ProblemReporting problemReporting,
    _Declaration declaration,
  ) {
    if (declaration.isAugment) {
      if (declaration is T && declaration.kind == _declaration.kind) {
        // Example:
        //
        //    class A {
        //      A();
        //      augment A();
        //    }
        //
        _augmentations.add(declaration);
        return true;
      } else {
        // Example:
        //
        //    class A {
        //      A();
        //      augment void A() {}
        //    }
        //
        // TODO(johnniwinther): Report augmentation conflict.
        return false;
      }
    } else {
      // Example:
      //
      //    class A {
      //      A();
      //      A();
      //    }
      //
      _declaration.reportDuplicateDeclaration(problemReporting, declaration);
      return false;
    }
  }

  @override
  void checkFragment(
    ProblemReporting problemReporting,
    _Declaration nonConstructorDeclaration,
  ) {
    // Check conflict with non-constructor.
    if (nonConstructorDeclaration.isStatic) {
      // Examples:
      //
      //    class A {
      //      A.foo();
      //      static void foo() {}
      //    }
      //
      // and
      //
      //    class A {
      //      factory A.foo() => throw '';
      //      static void foo() {}
      //    }
      //
      _declaration.reportConstructorConflict(
        problemReporting,
        nonConstructorDeclaration,
      );
    }
  }
}

abstract class _Declaration {
  final _DeclarationKind kind;
  final String displayName;
  final bool isAugment;
  final bool inPatch;
  final bool inLibrary;
  final bool isStatic;

  _Declaration(
    this.kind, {
    required this.displayName,
    required this.isAugment,
    required this.inPatch,
    required this.inLibrary,
    this.isStatic = true,
  });

  UriOffsetLength get uriOffset;

  void registerPreBuilder(
    ProblemReporting problemReporting,
    List<_PreBuilder> nonConstructorPreBuilders,
    List<_PreBuilder> constructorPreBuilders,
  );

  void reportConstructorConflict(
    ProblemReporting problemReporting,
    _Declaration declaration,
  );

  /// Reports that [declaration] conflicts with this declaration.
  void reportDuplicateDeclaration(
    ProblemReporting problemReporting,
    _Declaration declaration,
  );

  /// Adds this declaration to [thesePreBuilders] and checks it against the
  /// [otherPreBuilders].
  ///
  /// If this declaration can be absorbed into an existing declaration in
  /// [thesePreBuilders], it is added to the corresponding [_PreBuilder].
  /// Otherwise a new [_PreBuilder] is created and added to [thesePreBuilders].
  void _addPreBuilder(
    ProblemReporting problemReporting,
    List<_PreBuilder> thesePreBuilders,
    List<_PreBuilder> otherPreBuilders,
  ) {
    for (_PreBuilder existingPreBuilder in thesePreBuilders) {
      if (existingPreBuilder.absorbFragment(problemReporting, this)) {
        return;
      }
    }
    _checkAugmentation(problemReporting, this);
    thesePreBuilders.add(_createPreBuilder());
    if (otherPreBuilders.isNotEmpty) {
      otherPreBuilders.first.checkFragment(problemReporting, this);
    }
  }

  /// Creates the [_PreBuilder] for this [_Declaration].
  ///
  /// This is called for the declarations that aren't absorbed into a
  /// pre-existing declaration.
  _PreBuilder _createPreBuilder();
}

enum _DeclarationKind {
  Constructor,
  Factory,
  Class,
  Mixin,
  NamedMixinApplication,
  Enum,
  Extension,
  ExtensionType,
  Typedef,
  Method,
  Property,
}

/// [_PreBuilder] for non-constructor, non-property declarations.
class _DeclarationPreBuilder extends _PreBuilder {
  final _StandardDeclaration _declaration;
  final List<_StandardDeclaration> _augmentations = [];

  // TODO(johnniwinther): Report error if [fragment] is augmenting.
  _DeclarationPreBuilder(this._declaration);

  @override
  bool absorbFragment(
    ProblemReporting problemReporting,
    _Declaration declaration,
  ) {
    if (declaration.isAugment) {
      if (declaration.kind == _declaration.kind) {
        // Example:
        //
        //    class Foo {}
        //    augment class Foo {}
        //
        _augmentations.add(declaration as _StandardDeclaration);
        return true;
      } else {
        // Example:
        //
        //    class Foo {}
        //    augment extension Foo {}
        //
        // TODO(johnniwinther): Report augmentation conflict.
        return false;
      }
    } else {
      // Examples:
      //
      //    class Foo {}
      //    set Foo(_) {}
      //
      // and
      //
      //    class Foo {}
      //    class Foo {}
      //
      _declaration.reportDuplicateDeclaration(problemReporting, declaration);
      return false;
    }
  }

  @override
  void checkFragment(
    ProblemReporting problemReporting,
    _Declaration constructorDeclaration,
  ) {
    // Check conflict with constructor.
    if (_declaration.isStatic) {
      // Examples:
      //
      //    class A {
      //      static void foo() {}
      //      A.foo();
      //    }
      //
      // and
      //
      //    class A {
      //      static void foo() {}
      //      factory A.foo() => throw '';
      //    }
      //
      _declaration.reportConstructorConflict(
        problemReporting,
        constructorDeclaration,
      );
    }
  }

  @override
  void createBuilders(BuilderFactory builderFactory) {
    builderFactory._createBuilder(
      _declaration._fragment,
      augmentations: _augmentations.map((f) => f._fragment).toList(),
    );
  }
}

mixin _DeclarationReportingMixin implements _Declaration {
  @override
  void reportDuplicateDeclaration(
    ProblemReporting problemReporting,
    _Declaration declaration,
  ) {
    // TODO(johnniwinther): Mark [declaration] as a duplicate so we don't
    //  report duplicates on duplicates.
    _reportDuplicateDeclaration(
      problemReporting,
      name: displayName,
      existingUriOffset: uriOffset,
      newUriOffset: declaration.uriOffset,
      existingKind: _getExistingKindForDuplicate(declaration),
      newIsSetter:
          declaration is _PropertyDeclaration &&
          declaration.propertyKind == _PropertyKind.Setter,
    );
  }

  _ExistingKind _getExistingKindForDuplicate(_Declaration declaration) =>
      _ExistingKind.Getable;

  void _reportDuplicateDeclaration(
    ProblemReporting problemReporting, {
    required String name,
    required UriOffsetLength existingUriOffset,
    required UriOffsetLength newUriOffset,
    required _ExistingKind existingKind,
    required bool newIsSetter,
  }) {
    switch (existingKind) {
      case _ExistingKind.Getable:
        if (newIsSetter) {
          problemReporting.addProblem2(
            codeSetterConflictsWithDeclaration.withArgumentsOld(name),
            newUriOffset,
            context: [
              codeSetterConflictsWithDeclarationCause
                  .withArgumentsOld(name)
                  .withLocation2(existingUriOffset),
            ],
          );
          return;
        }
        break;
      case _ExistingKind.ExplicitSetter:
        if (!newIsSetter) {
          problemReporting.addProblem2(
            codeDeclarationConflictsWithSetter.withArgumentsOld(name),
            newUriOffset,
            context: <LocatedMessage>[
              codeDeclarationConflictsWithSetterCause
                  .withArgumentsOld(name)
                  .withLocation2(existingUriOffset),
            ],
          );
          return;
        }
        break;
      case _ExistingKind.ImplicitSetter:
        problemReporting.addProblem2(
          codeConflictsWithImplicitSetter.withArgumentsOld(name),
          newUriOffset,
          context: [
            codeConflictsWithImplicitSetterCause
                .withArgumentsOld(name)
                .withLocation2(existingUriOffset),
          ],
        );
        return;
    }

    problemReporting.addProblem2(
      codeDuplicatedDeclaration.withArgumentsOld(name),
      newUriOffset,
      context: <LocatedMessage>[
        codeDuplicatedDeclarationCause
            .withArgumentsOld(name)
            .withLocation2(existingUriOffset),
      ],
    );
  }
}

enum _ExistingKind { Getable, ExplicitSetter, ImplicitSetter }

class _FactoryConstructorDeclaration extends _ConstructorDeclaration
    with _DeclarationReportingMixin {
  final String _name;
  final FactoryDeclaration _declaration;

  _FactoryConstructorDeclaration(
    this._declaration, {
    required String name,
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    required super.isConst,
    required super.uriOffset,
  }) : _name = name,
       super(_DeclarationKind.Factory);

  @override
  void reportConstructorConflict(
    ProblemReporting problemReporting,
    _Declaration nonConstructorDeclaration,
  ) {
    // Example:
    //
    //    class A {
    //      factory A.foo() => throw '';
    //      static void foo() {}
    //    }
    //
    problemReporting.addProblem2(
      codeMemberConflictsWithFactory.withArgumentsOld(displayName),
      nonConstructorDeclaration.uriOffset,
      context: [
        codeMemberConflictsWithFactoryCause
            .withArgumentsOld(displayName)
            .withLocation2(uriOffset),
      ],
    );
  }

  @override
  _PreBuilder _createPreBuilder() =>
      new _FactoryConstructorPreBuilder(_name, this);
}

class _FactoryConstructorPreBuilder
    extends _ConstructorPreBuilder<_FactoryConstructorDeclaration> {
  final String _name;

  _FactoryConstructorPreBuilder(this._name, super._declaration);

  @override
  void createBuilders(BuilderFactory builderFactory) {
    builderFactory._createFactoryBuilderFromDeclarations(
      _declaration._declaration,
      _augmentations.map((a) => a._declaration).toList(),
      name: _name,
      uriOffset: _declaration.uriOffset,
      isConst: _declaration.isConst,
      inPatch: _declaration.inPatch,
    );
  }
}

class _FieldDeclaration extends _PropertyDeclaration
    with _DeclarationReportingMixin {
  _FieldDeclaration({
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    required super.propertyKind,
    required super.declarations,
    required super.uriOffset,
    super.isStatic,
  });

  @override
  _PreBuilder _createPreBuilder() => new _PropertyPreBuilder.forField(this);

  @override
  _ExistingKind _getExistingKindForDuplicate(_Declaration declaration) {
    bool newIsSetter =
        declaration is _PropertyDeclaration &&
        declaration.propertyKind == _PropertyKind.Setter;
    return newIsSetter ? _ExistingKind.ImplicitSetter : _ExistingKind.Getable;
  }
}

mixin _FragmentDeclarationMixin implements _Declaration {
  @override
  UriOffsetLength get uriOffset => _fragment.uriOffset;

  Fragment get _fragment;

  @override
  String toString() => _fragment.toString();
}

class _GenerativeConstructorDeclaration extends _ConstructorDeclaration
    with _DeclarationReportingMixin {
  final String _name;
  final ConstructorDeclaration _declaration;

  _GenerativeConstructorDeclaration(
    this._declaration, {
    required String name,
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    required super.isConst,
    required super.uriOffset,
  }) : _name = name,
       super(_DeclarationKind.Constructor);

  @override
  void reportConstructorConflict(
    ProblemReporting problemReporting,
    _Declaration nonConstructorDeclaration,
  ) {
    // Example:
    //
    //    class A {
    //      A.foo();
    //      static void foo() {}
    //    }
    //
    problemReporting.addProblem2(
      codeMemberConflictsWithConstructor.withArgumentsOld(displayName),
      nonConstructorDeclaration.uriOffset,
      context: [
        codeMemberConflictsWithConstructorCause
            .withArgumentsOld(displayName)
            .withLocation2(uriOffset),
      ],
    );
  }

  @override
  _PreBuilder _createPreBuilder() =>
      new _GenerativeConstructorPreBuilder(_name, this);
}

class _GenerativeConstructorPreBuilder
    extends _ConstructorPreBuilder<_GenerativeConstructorDeclaration> {
  final String _name;

  _GenerativeConstructorPreBuilder(this._name, super._declaration);

  @override
  void createBuilders(BuilderFactory builderFactory) {
    builderFactory._createConstructorBuilderFromDeclarations(
      _declaration._declaration,
      _augmentations.map((a) => a._declaration).toList(),
      name: _name,
      uriOffset: _declaration.uriOffset,
      isConst: _declaration.isConst,
      inPatch: _declaration.inPatch,
    );
  }
}

class _GetterDeclaration extends _PropertyDeclaration
    with _DeclarationReportingMixin {
  _GetterDeclaration({
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    required super.propertyKind,
    required super.declarations,
    required super.uriOffset,
    super.isStatic,
  });

  @override
  _PreBuilder _createPreBuilder() => new _PropertyPreBuilder.forGetter(this);
}

abstract class _NonConstructorDeclaration extends _Declaration {
  _NonConstructorDeclaration(
    super.kind, {
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    super.isStatic,
  });

  @override
  void registerPreBuilder(
    ProblemReporting problemReporting,
    List<_PreBuilder> nonConstructorPreBuilders,
    List<_PreBuilder> constructorPreBuilders,
  ) {
    _addPreBuilder(
      problemReporting,
      nonConstructorPreBuilders,
      constructorPreBuilders,
    );
  }

  @override
  void reportConstructorConflict(
    ProblemReporting problemReporting,
    _Declaration constructorDeclaration,
  ) {
    if (constructorDeclaration.kind == _DeclarationKind.Constructor) {
      // Example:
      //
      //    class A {
      //      static int get foo => 42;
      //      A.foo();
      //    }
      //
      problemReporting.addProblem2(
        codeConstructorConflictsWithMember.withArgumentsOld(displayName),
        constructorDeclaration.uriOffset,
        context: [
          codeConstructorConflictsWithMemberCause
              .withArgumentsOld(displayName)
              .withLocation2(uriOffset),
        ],
      );
    } else {
      assert(
        constructorDeclaration.kind == _DeclarationKind.Factory,
        "Unexpected constructor kind $constructorDeclaration",
      );
      // Example:
      //
      //    class A {
      //      static int get foo => 42;
      //      factory A.foo() => throw '';
      //    }
      //
      problemReporting.addProblem2(
        codeFactoryConflictsWithMember.withArgumentsOld(displayName),
        constructorDeclaration.uriOffset,
        context: [
          codeFactoryConflictsWithMemberCause
              .withArgumentsOld(displayName)
              .withLocation2(uriOffset),
        ],
      );
    }
  }
}

/// A [_PreBuilder] is a precursor to a [Builder] with subclasses for
/// properties, constructors, and other declarations.
sealed class _PreBuilder {
  /// Tries to include [declaration] in this [_PreBuilder].
  ///
  /// If [declaration] can be absorbed, `true` is returned. Otherwise an error
  /// is reported and `false` is returned.
  bool absorbFragment(
    ProblemReporting problemReporting,
    _Declaration declaration,
  );

  /// Checks with [declaration] conflicts with this [_PreBuilder].
  ///
  /// This is called between constructors and non-constructors which do not
  /// occupy the same name space but can only co-exist if the non-constructor
  /// is not static.
  void checkFragment(
    ProblemReporting problemReporting,
    _Declaration declaration,
  );

  /// Creates [Builder]s for the fragments absorbed into this [_PreBuilder],
  /// using [BuilderFactory] to create a [Builder] for a single [Fragment].
  ///
  /// If `conflictingSetter` is `true`, the created [Builder] must be marked
  /// as a conflicting setter. This is needed to ensure that we don't create
  /// conflicting AST nodes: Normally we only create [Builder]s for
  /// non-duplicate declarations, but because setters are store in a separate
  /// map the [NameSpace], they are not directly marked as duplicate if they
  /// do not conflict with other setters.
  void createBuilders(BuilderFactory builderFactory);
}

abstract class _PropertyDeclaration extends _NonConstructorDeclaration {
  final _PropertyKind propertyKind;
  final _PropertyDeclarations declarations;

  @override
  final UriOffsetLength uriOffset;

  _PropertyDeclaration({
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    required this.propertyKind,
    required this.declarations,
    required this.uriOffset,
    super.isStatic,
  }) : super(_DeclarationKind.Property);

  void reportStaticInstanceConflict(
    ProblemReporting problemReporting,
    _PropertyDeclaration declaration,
  ) {
    if (isStatic) {
      problemReporting.addProblem2(
        codeInstanceConflictsWithStatic.withArgumentsOld(displayName),
        declaration.uriOffset,
        context: [
          codeInstanceConflictsWithStaticCause
              .withArgumentsOld(displayName)
              .withLocation2(uriOffset),
        ],
      );
    } else {
      problemReporting.addProblem2(
        codeStaticConflictsWithInstance.withArgumentsOld(displayName),
        declaration.uriOffset,
        context: [
          codeStaticConflictsWithInstanceCause
              .withArgumentsOld(displayName)
              .withLocation2(uriOffset),
        ],
      );
    }
  }
}

class _PropertyDeclarations {
  final FieldDeclaration? field;
  final GetterDeclaration? getter;
  final SetterDeclaration? setter;

  _PropertyDeclarations({this.field, this.getter, this.setter});
}

enum _PropertyKind { Getter, Setter, Field, FinalField }

/// [_PreBuilder] for properties, i.e. fields, getters and setters.
class _PropertyPreBuilder extends _PreBuilder {
  final bool inPatch;
  final String name;
  final UriOffsetLength uriOffset;
  final bool isStatic;
  _PropertyDeclaration? _getterDeclaration;
  _PropertyDeclaration? _setterDeclaration;
  List<GetterDeclaration> _getterAugmentations = [];
  List<SetterDeclaration> _setterAugmentations = [];

  // TODO(johnniwinther): Report error if [field] is augmenting.
  _PropertyPreBuilder.forField(_PropertyDeclaration field)
    : isStatic = field.isStatic,
      inPatch = field.inPatch,
      name = field.displayName,
      uriOffset = field.uriOffset,
      _getterDeclaration = field,
      _setterDeclaration = field.propertyKind == _PropertyKind.Field
          ? field
          : null {
    _PropertyDeclarations declarations = field.declarations;
    assert(
      declarations.field != null,
      "Unexpected field declaration from field ${field}.",
    );
    assert(
      declarations.getter != null,
      "Unexpected getter declaration from field ${field}.",
    );
    assert(
      (declarations.setter != null) ==
          (_getterDeclaration!.propertyKind == _PropertyKind.Field),
      "Unexpected setter declaration from field ${field}.",
    );
  }

  // TODO(johnniwinther): Report error if [getter] is augmenting.
  _PropertyPreBuilder.forGetter(_PropertyDeclaration getter)
    : isStatic = getter.isStatic,
      inPatch = getter.inPatch,
      name = getter.displayName,
      uriOffset = getter.uriOffset,
      _getterDeclaration = getter {
    _PropertyDeclarations declarations = getter.declarations;
    assert(
      declarations.field == null,
      "Unexpected field declaration from getter ${getter}.",
    );
    assert(
      declarations.getter != null,
      "Unexpected getter declaration from getter ${getter}.",
    );
    assert(
      declarations.setter == null,
      "Unexpected setter declaration from getter ${getter}.",
    );
  }

  // TODO(johnniwinther): Report error if [setter] is augmenting.
  _PropertyPreBuilder.forSetter(_PropertyDeclaration setter)
    : isStatic = setter.isStatic,
      inPatch = setter.inPatch,
      name = setter.displayName,
      uriOffset = setter.uriOffset,
      _setterDeclaration = setter {
    _PropertyDeclarations declarations = setter.declarations;
    assert(
      declarations.field == null,
      "Unexpected field declaration from setter ${setter}.",
    );
    assert(
      declarations.getter == null,
      "Unexpected getter declaration from setter ${setter}.",
    );
    assert(
      declarations.setter != null,
      "Unexpected setter declaration from setter ${setter}.",
    );
  }

  @override
  bool absorbFragment(
    ProblemReporting problemReporting,
    _Declaration declaration,
  ) {
    if (declaration is! _PropertyDeclaration) {
      if (_getterDeclaration != null) {
        // Example:
        //
        //    int get foo => 42;
        //    void foo() {}
        //
        _getterDeclaration!.reportDuplicateDeclaration(
          problemReporting,
          declaration,
        );
      } else {
        assert(_setterDeclaration != null);
        // Example:
        //
        //    void set foo(_) {}
        //    void foo() {}
        //
        _setterDeclaration!.reportDuplicateDeclaration(
          problemReporting,
          declaration,
        );
      }
      return false;
    }

    _PropertyKind? propertyKind = declaration.propertyKind;
    switch (propertyKind) {
      case _PropertyKind.Getter:
        if (_getterDeclaration == null) {
          // Example:
          //
          //    void set foo(_) {}
          //    int get foo => 42;
          //
          if (declaration.isAugment) {
            // Example:
            //
            //    void set foo(_) {}
            //    augment int get foo => 42;
            //
            // TODO(johnniwinther): Report error.
          }
          if (declaration.isStatic != isStatic) {
            // Examples:
            //
            //    class A {
            //      void set foo(_) {}
            //      static int get foo => 42;
            //    }
            //
            // and
            //
            //    class A {
            //      static void set foo(_) {}
            //      int get foo => 42;
            //    }
            //
            _setterDeclaration!.reportStaticInstanceConflict(
              problemReporting,
              declaration,
            );
            return false;
          } else {
            _PropertyDeclarations declarations = declaration.declarations;
            assert(
              declarations.field == null,
              "Unexpected field declaration from getter "
              "${declaration}.",
            );
            assert(
              declarations.setter == null,
              "Unexpected setter declaration from getter "
              "${declaration}.",
            );
            _getterDeclaration = declaration;
            return true;
          }
        } else {
          if (declaration.isAugment) {
            // Example:
            //
            //    int get foo => 42;
            //    augment int get foo => 87;
            //
            _PropertyDeclarations declarations = declaration.declarations;
            assert(
              declarations.field == null,
              "Unexpected field declaration from getter "
              "${declaration}.",
            );
            assert(
              declarations.setter == null,
              "Unexpected setter declaration from getter "
              "${declaration}.",
            );
            _getterAugmentations.add(declarations.getter!);
            return true;
          } else {
            // Example:
            //
            //    int get foo => 42;
            //    int get foo => 87;
            //
            _getterDeclaration!.reportDuplicateDeclaration(
              problemReporting,
              declaration,
            );
            return false;
          }
        }
      case _PropertyKind.Setter:
        if (_setterDeclaration == null) {
          // Examples:
          //
          //    int get foo => 42;
          //    void set foo(_) {}
          //
          //    final int bar = 42;
          //    void set bar(_) {}
          //
          if (declaration.isAugment) {
            // Example:
            //
            //    int get foo => 42;
            //    augment void set foo(_) {}
            //
            // TODO(johnniwinther): Report error.
          }
          if (declaration.isStatic != isStatic) {
            // Examples:
            //
            //    class A {
            //      int get foo => 42;
            //      static void set foo(_) {}
            //    }
            //
            // and
            //
            //    class A {
            //      static int get foo => 42;
            //      void set foo(_) {}
            //    }
            //
            _getterDeclaration!.reportStaticInstanceConflict(
              problemReporting,
              declaration,
            );
            return false;
          } else {
            _PropertyDeclarations declarations = declaration.declarations;
            assert(
              declarations.field == null,
              "Unexpected field declaration from setter "
              "${declaration}.",
            );
            assert(
              declarations.getter == null,
              "Unexpected getter declaration from setter "
              "${declaration}.",
            );
            _setterDeclaration = declaration;
            return true;
          }
        } else {
          if (declaration.isAugment) {
            // Example:
            //
            //    void set foo(_) {}
            //    augment void set foo(_) {}
            //
            _PropertyDeclarations declarations = declaration.declarations;
            assert(
              declarations.field == null,
              "Unexpected field declaration from setter "
              "${declaration}.",
            );
            assert(
              declarations.getter == null,
              "Unexpected getter declaration from setter "
              "${declaration}.",
            );
            _setterAugmentations.add(declarations.setter!);
            return true;
          } else {
            // Examples:
            //
            //    int? foo;
            //    void set foo(_) {}
            //
            // and
            //
            //    void set foo(_) {}
            //    void set foo(_) {}
            //
            _setterDeclaration!.reportDuplicateDeclaration(
              problemReporting,
              declaration,
            );
            return false;
          }
        }
      case _PropertyKind.Field:
        if (_getterDeclaration == null) {
          // Example:
          //
          //    void set foo(_) {}
          //    int? foo;
          //
          assert(_getterDeclaration == null && _setterDeclaration != null);
          // We have an explicit setter.
          _setterDeclaration!.reportDuplicateDeclaration(
            problemReporting,
            declaration,
          );
          return false;
        } else if (_setterDeclaration != null) {
          // Examples:
          //
          //    int? foo;
          //    int? foo;
          //
          //    int get bar => 42;
          //    void set bar(_) {}
          //    int bar = 87;
          //
          //    final int baz = 42;
          //    void set baz(_) {}
          //    int baz = 87;
          //
          assert(_getterDeclaration != null && _setterDeclaration != null);
          // We have both getter and setter
          if (declaration.isAugment) {
            // Coverage-ignore-block(suite): Not run.
            if (_getterDeclaration!.propertyKind == declaration.propertyKind) {
              // Example:
              //
              //    int foo = 42;
              //    augment int foo = 87;
              //
              _PropertyDeclarations declarations = declaration.declarations;
              // TODO(johnniwinther): Handle field augmentation.
              _getterAugmentations.add(declarations.getter!);
              _setterAugmentations.add(declarations.setter!);
              return true;
            } else {
              // Example:
              //
              //    final int foo = 42;
              //    void set foo(_) {}
              //    augment int foo = 87;
              //
              // TODO(johnniwinther): Report error.
              // TODO(johnniwinther): Should the augment be absorbed in this
              //  case, as an erroneous augmentation?
              return false;
            }
          } else {
            // Examples:
            //
            //    int? foo;
            //    int? foo;
            //
            //    int? get bar => null;
            //    void set bar(_) {}
            //    int? bar;
            //
            _getterDeclaration!.reportDuplicateDeclaration(
              problemReporting,
              declaration,
            );
            return false;
          }
        } else {
          // Examples:
          //
          //    int get foo => 42;
          //    int? foo;
          //
          //    final int bar = 42;
          //    int? bar;
          //
          assert(_getterDeclaration != null && _setterDeclaration == null);
          _getterDeclaration!.reportDuplicateDeclaration(
            problemReporting,
            declaration,
          );
          return false;
        }
      case _PropertyKind.FinalField:
        if (_getterDeclaration == null) {
          // Example:
          //
          //    void set foo(_) {}
          //    final int foo = 42;
          //
          assert(_getterDeclaration == null && _setterDeclaration != null);
          // We have an explicit setter.
          if (declaration.isAugment) {
            // Example:
            //
            //    void set foo(_) {}
            //    augment final int foo = 42;
            //
            // TODO(johnniwinther): Report error.
          }
          if (declaration.isStatic != isStatic) {
            // Coverage-ignore-block(suite): Not run.
            // Examples:
            //
            //    class A {
            //      void set foo(_) {}
            //      static final int foo = 42;
            //    }
            //
            // and
            //
            //    class A {
            //      static void set foo(_) {}
            //      final int foo = 42;
            //    }
            //
            _setterDeclaration!.reportStaticInstanceConflict(
              problemReporting,
              declaration,
            );
            return false;
          } else {
            _PropertyDeclarations declarations = declaration.declarations;
            assert(
              declarations.setter == null,
              "Unexpected setter declaration from field "
              "${declaration}.",
            );
            _getterDeclaration = declaration;
            return true;
          }
        } else {
          // Examples:
          //
          //    final int foo = 42;
          //    final int foo = 87;
          //
          //    int get bar => 42;
          //    final int bar = 87;
          //
          if (declaration.isAugment) {
            // Coverage-ignore-block(suite): Not run.
            if (_getterDeclaration!.propertyKind == declaration.propertyKind) {
              // Example:
              //
              //    final int foo = 42;
              //    augment final int foo = 87;
              //
              _PropertyDeclarations declarations = declaration.declarations;
              assert(
                declarations.setter == null,
                "Unexpected setter declaration from final field "
                "${declaration}.",
              );
              // TODO(johnniwinther): Handle field augmentation.
              _getterAugmentations.add(declarations.getter!);
              return true;
            } else {
              // Example:
              //
              //    int foo = 42;
              //    augment final int foo = 87;
              //
              // TODO(johnniwinther): Report error.
              // TODO(johnniwinther): Should the augment be absorbed in this
              //  case, as an erroneous augmentation?
              return false;
            }
          } else {
            // Examples:
            //
            //    final int foo = 42;
            //    final int foo = 87;
            //
            //    int get bar => 42;
            //    final int bar = 87;
            //
            _getterDeclaration!.reportDuplicateDeclaration(
              problemReporting,
              declaration,
            );
            return false;
          }
        }
    }
  }

  @override
  void checkFragment(
    ProblemReporting problemReporting,
    _Declaration constructorDeclaration,
  ) {
    // Check conflict with constructor.
    if (isStatic) {
      if (_getterDeclaration != null) {
        // Examples:
        //
        //    class A {
        //      static int get foo => 42;
        //      A.foo();
        //    }
        //
        // and
        //
        //    class A {
        //      static int get foo => 42;
        //      factory A.foo() => throw '';
        //    }
        //
        _getterDeclaration!.reportConstructorConflict(
          problemReporting,
          constructorDeclaration,
        );
      } else {
        // Coverage-ignore-block(suite): Not run.
        // Examples:
        //
        //    class A {
        //      static void set foo(_) {}
        //      A.foo();
        //    }
        //
        // and
        //
        //    class A {
        //      static void set foo(_) {}
        //      factory A.foo() => throw '';
        //    }
        //
        _setterDeclaration!.reportConstructorConflict(
          problemReporting,
          constructorDeclaration,
        );
      }
    }
  }

  @override
  void createBuilders(BuilderFactory builderFactory) {
    builderFactory._createPropertyBuilder(
      name: name,
      inPatch: inPatch,
      isStatic: isStatic,
      uriOffset: uriOffset,
      fieldDeclaration: _getterDeclaration?.declarations.field,
      getterDeclaration: _getterDeclaration?.declarations.getter,
      getterAugmentations: _getterAugmentations,
      setterDeclaration: _setterDeclaration?.declarations.setter,
      setterAugmentations: _setterAugmentations,
    );
  }
}

class _SetterDeclaration extends _PropertyDeclaration
    with _DeclarationReportingMixin {
  _SetterDeclaration({
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    required super.propertyKind,
    required super.declarations,
    required super.uriOffset,
    super.isStatic,
  });

  @override
  _PreBuilder _createPreBuilder() => new _PropertyPreBuilder.forSetter(this);

  @override
  _ExistingKind _getExistingKindForDuplicate(_Declaration declaration) {
    return _ExistingKind.ExplicitSetter;
  }
}

abstract class _StandardDeclaration extends _NonConstructorDeclaration {
  _StandardDeclaration(
    super.kind, {
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    super.isStatic,
  });

  // TODO(johnniwinther): Remove this.
  Fragment get _fragment;
}

class _StandardFragmentDeclaration extends _StandardDeclaration
    with
        _DeclarationReportingMixin,
        _FragmentDeclarationMixin,
        _StandardFragmentDeclarationMixin {
  @override
  final Fragment _fragment;

  _StandardFragmentDeclaration(
    super.kind,
    this._fragment, {
    required super.displayName,
    required super.isAugment,
    required super.inPatch,
    required super.inLibrary,
    super.isStatic,
  });

  @override
  _PreBuilder _createPreBuilder() => new _DeclarationPreBuilder(this);
}

mixin _StandardFragmentDeclarationMixin implements _StandardDeclaration {
  @override
  Fragment get _fragment;
}
